\chapter{GNU Regex正则库应用案例}
本章根据\ref{intro-sec-regex-cases}节中整数有效性判断、电话号码有效性判断、
整数重复数字判断、找最亲近同学、比压牌和牌型分类6典型应用案例的需求，
分别使用GNU Regex库的GNU界面和POSIX界面进行了正则编程实现，但未使用BSD界面进行实现。

\section{整数范围判断}

\subsection{GNU正则判断}
正整数字符串有效性判断的GNU用户界面实现如代码\ref{code-int-range-gnu}所示。

\cvfile[label=code-int-range-gnu]{GNU正则判断正整数字符串有效性}%
  {gnu-regex/codes/case-study/int-range-gnu.c}

在代码\ref{code-int-range-gnu}中，第39行使用\cinline{memset()}函数对正则缓冲区
进行了清0操作，也就是将其所有成员初始化为了0。由于该问题并不关注匹配的位置，
因此，在第52行的\cinline{re_match()} 函数调用中，为其匹配寄存器形参提供了%
\cinline{NULL}实参。

\subsection{POSIX正则判断}
正整数字符串有效性判断的POSIX用户界面实现如代码\ref{code-int-range-posix}所示。

\cvfile[label=code-int-range-posix]{POSIX正则判断正整数字符串有效性}%
  {gnu-regex/codes/case-study/int-range-posix.c}

在代码\ref{code-int-range-posix}中，第36行使用\cinline{regcomp()}函数编译正则
表达式时，通过\cinline{'|'}按位\enquote{或}的方式设置了\cinline{REG_NEWLINE | REG_EXTENDED}%
正则语法。由于该问题并不关注匹配的位置，因此，在第43行的\cinline{regexec()} 函数
调用中，为其匹配寄存器形参提供了长度为0，指针为\cinline{NULL}的实参。

\section{电话号码判断}

\subsection{GNU正则判断}
城市电话号码有效性判断的GNU用户界面实现如代码\ref{code-phonenum-gnu}所示。

\cvfile[label=code-phonenum-gnu]{GNU正则判断电话号码字符串有效性}%
  {gnu-regex/codes/case-study/phonenum-check-gnu.c}

在代码\ref{code-phonenum-gnu}中，第41行使用\cinline{memset()}函数对正则缓冲区
进行了清0操作，也就是将其所有成员初始化为了0。由于该问题并不关注匹配的位置，
因此，在第57行的\cinline{re_match()} 函数调用中，为其匹配寄存器形参提供了%
\cinline{NULL}实参。

\subsection{POSIX正则判断}
城市电话号码有效性判断的POSIX用户界面实现如代码\ref{code-phonenum-posix}所示。

\cvfile[label=code-phonenum-posix]{POSIX正则判断电话号码字符串有效性}%
  {gnu-regex/codes/case-study/phonenum-check-posix.c}

在代码\ref{code-phonenum-posix}中，第41行使用\cinline{regcomp()}函数编译正则
表达式时，通过\cinline{'|'}按位或的方式设置了\cinline{REG_NEWLINE | REG_EXTENDED}%
正则语法。由于该问题并不关注匹配的位置，因此，在第48行的\cinline{regexec()} 函数
调用中，为其匹配寄存器形参提供了长度为0，指针为\cinline{NULL}的实参。

\section{判断重复数字}

\subsection{GNU正则判断}
判断整数中的重复数字的GNU用户界面实现如代码\ref{code-repetition-gnu}所示。

\cvfile[label=code-repetition-gnu]{GNU正则判断并显示重复数字}%
  {gnu-regex/codes/case-study/repetition-numb-gnu.c}

在代码\ref{code-repetition-gnu}中，第42行的正则表达式\cinline{"([0-9])\\1+"}中，
使用\cinline{"\1+"}实现了分组的反向引用，在C语言中，需要使用\cinline{"\\"}%
实现\verb|'\'|的转义操作。在第66行用\cinline{memset()}函数实现了正则缓存的初始化。
第82---108行的恒为真的循环实现正则表达式的重复搜索。第88---89行为%
\cinline{re_search()}函数传入了匹配寄存器的地址\cinline{&regs}，第105行取得默认
分组(group 0)的结束位置作为下次搜索的开始位置。第85行对原被搜索字符串进行偏移后
进入下一次搜索。一旦搜索失败，则退出循环(第91---94行)。

\subsection{POSIX正则判}
判断整数中的重复数字的POSIX用户界面实现如代码\ref{code-repetition-posix}所示。

\cvfile[label=code-repetition-posix]{POSIX正则判断并显示重复数字}%
  {gnu-regex/codes/case-study/repetition-numb-posix.c}

在代码\ref{code-repetition-posix}中，第76---101行的恒为真的循环实现正则表达式的
重复搜索。第88---89行为\cinline{regexec()}函数传入了匹配寄存器的长度%
\cinline{nmatchs}和数组首地址\cinline{pmatch}。第98行取得默认分组(group 0)的结束
位置作为下次搜索的开始位置。第79行对原被搜索字符串进行偏移后进入下一次搜索。
一旦搜索失败，则退出循环(第84---86行)。

\section{寻找亲近同学}

\subsection{GNU正则寻找}
寻找亲近同学的GNU用户界面实现如代码\ref{code-findclosest-gnu}所示。

\cvfile[label=code-findclosest-gnu]{GNU正则寻找亲近同学}%
  {gnu-regex/codes/case-study/findclosest-gnu.c}

在代码\ref{code-findclosest-gnu}中，第51---62行采用动态内存分配的方式，根据传入
的喜欢类型字符串构造了需要正则搜索的正则表达式。在第65行用\cinline{memset()}函数
实现了正则缓存的初始化。第80---104行的恒为真的循环实现正则表达式的重复搜索。
由于问题需求中，只可能发生单一一个字母的匹配，因此在103行为直接使用%
\cinline{re_search()}函数的返回值偏移到下一个字符，通过第83行的指针调整实现了重复
搜索。一旦搜索失败，则退出循环(第90---93行)。

\subsection{POSIX正则寻找}
寻找亲近同学的POSIX用户界面实现如代码\ref{code-findclosest-posix}所示。

\cvfile[label=code-findclosest-posix]{POSIX正则寻找亲近同学}%
  {gnu-regex/codes/case-study/findclosest-posix.c}

在代码\ref{code-findclosest-posix}中，第52---62行采用动态内存分配的方式，根据传入
的喜欢类型字符串构造了需要正则搜索的正则表达式。第80---105行的恒为真的循环实现
正则表达式的重复搜索。第84行为\cinline{regexec()}函数传入了匹配寄存器的长度%
\cinline{nmatchs}和数组首地址\cinline{pmatch}。第102行取得默认分组(group 0)的结束
位置作为下次搜索的开始位置。第83行对原被搜索字符串进行偏移后进入下一次搜索。
一旦搜索失败，则退出循环(第87---91行)。

\section{比压牌}

\subsection{GNU正则判断}
比压牌的GNU用户界面实现如代码\ref{code-judgepoker-gnu}所示。

\cvfile[label=code-judgepoker-gnu]{GNU正则判断比压牌}%
  {gnu-regex/codes/case-study/judgepoker-gnu.c}

在代码\ref{code-judgepoker-gnu}中，第47---56行构造了正则表达式查找表。
第58---65行，根据对方出牌的张数，在正则表达式查找表中取得需要正则搜索的
正则表达式。第68行用\cinline{memset()}函数实现了正则缓存的初始化。
由于该问题并不关注匹配位置，因此，在第80---81行的\cinline{re_search()} 正则搜索
函数调用中，为其匹配寄存器形参提供了\cinline{NULL}实参。

\subsection{POSIX正则判断}
比压牌的POSIX用户界面实现如代码\ref{code-judgepoker-posix}所示。

\cvfile[label=code-judgepoker-posix]{POSIX正则判断比压牌}%
  {gnu-regex/codes/case-study/judgepoker-posix.c}

在代码\ref{code-judgepoker-posix}中，第46---55行构造了正则表达式查找表。
第57---64行，根据对方出牌的张数，在正则表达式查找表中取得需要的
正则表达式。第68行用\cinline{memset()}函数实现了正则缓存的初始化。
由于该问题并不关注匹配的位置，因此，在第84行的\cinline{regexec()} 函数
调用中，为其匹配寄存器形参提供了长度为0，指针为\cinline{NULL}的实参。

\section{牌型分类}

\subsection{GNU正则分类}
牌型分类的GNU用户界面实现如代码\ref{code-classifypoker-gnu}所示。

\cvfile[label=code-classifypoker-gnu]{GNU正则牌型分类}%
  {gnu-regex/codes/case-study/classify-poker-5cards-gnu.c}

在代码\ref{code-classifypoker-gnu}中，第50---61行构造了正则表达式数组。
第64行用\cinline{memset()}函数实现了正则缓存的初始化。第67---88行通过遍历正则
表达式数组，通过正则搜索获得能够匹配的正则表达式下标索引。由于该问题并不关注
匹配位置，因此，在第78---79行的\cinline{re_search()} 正则搜索函数调用中，为其
匹配寄存器形参提供了\cinline{NULL}实参。

当然，如果所有正则表达式都无法匹配，则返回循环结束后的\cinline{i}值(第91行)。

\subsection{POSIX正则分类}
牌型分类的POSIX用户界面实现如代码\ref{code-classifypoker-posix}所示。

\cvfile[label=code-classifypoker-posix]{POSIX正则牌型分类}%
  {gnu-regex/codes/case-study/classify-poker-5cards-posix.c}

在代码\ref{code-classifypoker-posix}中，第48---59行构造了正则表达式数组。
第61---83行通过遍历正则表达式数组，通过正则搜索获得能够匹配的正则表达式下标索引。
由于该问题并不关注匹配的位置，因此，在第84行的\cinline{regexec()} 函数
调用中，为其匹配寄存器形参提供了长度为0，指针为\cinline{NULL}的实参。

当然，如果所有正则表达式都无法匹配，则返回循环结束后的\cinline{i}值(第86行)。
